import javax.media.j3d.*;
import javax.vecmath.*;
import com.sun.j3d.utils.geometry.*;
import com.sun.j3d.utils.universe.*;
import com.sun.j3d.utils.behaviors.vp.*;
import javax.swing.JFrame;


/**
* Beispiel fuer eine animierte dreidimensionale Szene
*
* @author Frank Klawonn
* Letzte Aenderung 27.05.2005
* @see StaticSceneExample
*/
public class SimpleAnimation3d extends JFrame
{

  //Der Canvas, auf den gezeichnet wird.
  public Canvas3D myCanvas3D;


  public SimpleAnimation3d()
  {
    //Mechanismus zum Schliessen des Fensters und beenden des Programms
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    //Standardeinstellung fuer das Betrachteruniversum
    myCanvas3D = new Canvas3D(SimpleUniverse.getPreferredConfiguration());


    //Aufbau des SimpleUniverse:
    //Zuerst Erzeugen zusammen mit dem Canvas
    SimpleUniverse simpUniv = new SimpleUniverse(myCanvas3D);


    //Standardpositionierung des Betrachters
    simpUniv.getViewingPlatform().setNominalViewingTransform();


    //Die Szene wird in dieser Methode erzeugt.
    createSceneGraph(simpUniv);


    //Hinzufuegen von Licht
    addLight(simpUniv);


    //Hierdurch kann man mit der Maus den Betrachterstandpunkt veraendern
    OrbitBehavior ob = new OrbitBehavior(myCanvas3D);
    ob.setSchedulingBounds(new BoundingSphere(new Point3d(0.0,0.0,0.0),Double.MAX_VALUE));
    simpUniv.getViewingPlatform().setViewPlatformBehavior(ob);


    //Darstellung des Canvas/Fensters:
    setTitle("Eine kleine Animation");
    setSize(700,700);
    getContentPane().add("Center", myCanvas3D);
    setVisible(true);

  }




  public static void main(String[] args)
  {
     SimpleAnimation3d sa = new SimpleAnimation3d();
  }


  //In dieser Methode werden die Objekte der Szene aufgebaut, die Bewegungen
  //definiert und dem SimpleUniverse su hinzugefuegt.
  public void createSceneGraph(SimpleUniverse su)
  {

//*** Die Plattform, auf der der Hubschrauber steht, ***
//*** und ihre Transformationsgruppe:                ***

      //Eine Appearance, um die Plattform Rot zu faerben
      Appearance redApp = new Appearance();
      setToMyDefaultAppearance(redApp,new Color3f(0.8f,0.0f,0.0f));


      //Halbe Seitenlaenge des Wuerfels, der die Plattform darstellt.
      float platformSize = 0.1f;

      //Erzeugung der Plattform als Wuerfel
      Box platform = new Box(platformSize,platformSize,platformSize,redApp);

      //Eine Transformation, die die Plattform etwas rotiert.
      Transform3D tfPlatform = new Transform3D();
      tfPlatform.rotY(Math.PI/6);

      //Die Transformationsgruppe der Plattform
      TransformGroup tgPlatform = new TransformGroup(tfPlatform);
      tgPlatform.addChild(platform);


//*** Die Kabine des Hubschraubers ****

      //Eine Appearance, um die Kabine Gruen zu faerben.
      Appearance greenApp = new Appearance();
      setToMyDefaultAppearance(greenApp,new Color3f(0.0f,0.7f,0.0f));

      //Radius der Kabine
      float cabinRadius = 0.1f;

      //Erzeugung der Kabine als Kugel
      Sphere cabin = new Sphere(cabinRadius,greenApp);

      //Die Transformationsgruppe der Kabine.
      //Die Kabine bleibt zunaechst im Koordinatenursprung. Es wird spaeter
      //der gesamte Hubschrauber auf die Plattform verschoben.
      TransformGroup tgCabin = new TransformGroup();
      tgCabin.addChild(cabin);




//*** Der Rotor des Hubschraubers mit Drehung ***

    //Eine Appearance, um den Rotor Blau zu faerben.
    Appearance blueApp = new Appearance();
    setToMyDefaultAppearance(blueApp,new Color3f(0.0f,0.0f,1.0f));

    //Erzeugung des Rotors als Quader
    Box rotor = new Box(0.4f,0.0001f,0.01f,blueApp);

    //Die Transformationsgruppe fuer den Rotor, in der auch die Rotation
    //modelliert wird.
    TransformGroup tgmRotor = new TransformGroup();
    tgmRotor.addChild(rotor);

    //Die langsame Startrotation des Rotors
    Transform3D bladeRotationAxis = new Transform3D();
    int timeStartRotor = 2000; //Der Rotor soll sich nach 2 Sekunden anfangen zu drehen.
    int noStartRotations = 2;  //Er fuehrt zunaechst 2 langsame Umdrehungen durch.
    int timeSlowRotation = 1500;//Eine langsame Umdrehung dauert 1.5 Sekunden.

    //Das Alpha fuer die langsame Rotation
    Alpha bladeRotationStartAlpha = new Alpha(noStartRotations,
                                              Alpha.INCREASING_ENABLE,
                                              timeStartRotor,
                                              0,timeSlowRotation,0,0,0,0,0);



    //Die langsame Rotation
    RotationInterpolator bladeRotationStart = new RotationInterpolator(
                                             bladeRotationStartAlpha,tgmRotor,
                                             bladeRotationAxis,0.0f,(float) Math.PI*2);

    BoundingSphere bounds = new BoundingSphere(new Point3d(0.0,0.0,0.0),Double.MAX_VALUE);
    bladeRotationStart.setSchedulingBounds(bounds);



    int timeFastRotation = 500; //Eine schnelle Umdrehung dauert 0.5 Sekunden.
    int timeOneWayFlight = 2000;//Der Hubschrauber soll in 2 Sekunden aufsteigen.
    int timeHovering = 1000;//Er soll zwei Sekunden in der Hoehe verharren.
    int timeStartWait = 1000;//Wenn der Rotor sich eine Sekunde lang schnell
                             //gedreht hat, hebt der Hubschrauber ab.

    //Die Gesamtzeit, wann der Hubschrauber abhebt.
    int timeFlightStart = timeStartRotor+timeSlowRotation*noStartRotations+timeStartWait;
    //Anzahl der schnellen Rotationen
    int noFastRotations = 1+ ((timeStartWait+2*timeOneWayFlight+timeHovering)/timeFastRotation);

    //Das Alpha fuer die schnelle Rotation.
    Alpha bladeRotationAlpha = new Alpha(noFastRotations,Alpha.INCREASING_ENABLE,
                                         timeStartRotor+timeSlowRotation*noStartRotations,
                                         0,timeFastRotation,0,0,0,0,0);


    //Die schnelle Rotationsbewegung
    RotationInterpolator bladeRotation = new RotationInterpolator(
                                             bladeRotationAlpha,tgmRotor,
                                             bladeRotationAxis,0.0f,(float) Math.PI*2);
    bladeRotation.setSchedulingBounds(bounds);


    //Die langsame und die schnelle Rotation werden der Transformationsgruppe
    //des Rotors hinzugefuegt. Diese Rotationen finden bisher noch im
    //Koordinatenursprung statt.
    tgmRotor.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
    tgmRotor.addChild(bladeRotationStart);
    tgmRotor.addChild(bladeRotation);


//*** Die ransformationsgruppe zur Positionierung des Rotors oben auf der Kabine ***
    //Die Rotordrehung wird damit ebenso vom Ursprung oben auf die Kabine
    //verlagert.
    Transform3D tfRotor = new Transform3D();
    tfRotor.setTranslation(new Vector3f(0.0f,cabinRadius,0.0f));
    TransformGroup tgRotor = new TransformGroup(tfRotor);
    tgRotor.addChild(tgmRotor);



//*** Das Heck des Hubschraubers ***

    //Laenge des Hecks
    float halfTailLength = 0.2f;

    //Das Heck wird als gruener Quader erzeugt.
    Box tail = new Box(halfTailLength,0.02f,0.02f,greenApp);

    //Eine Transformation, die das Heck an den Rand der Kabine verschiebt.
    Transform3D tfTail = new Transform3D();
    tfTail.setTranslation(new Vector3f(cabinRadius+halfTailLength,0.0f,0.0f));

    //Die Transformationsgruppe fuer das Heck
    TransformGroup tgTail = new TransformGroup(tfTail);
    tgTail.addChild(tail);



//*** Die Transformationsgruppe fuer die Bewegung des Hubschraubers. ***
    //Die Bewegung wird bzgl. des Kordinatenursprungs beschrieben und spaeter
    //auf die Plattform verschoben.
    TransformGroup tgmHelicopter = new TransformGroup();

    //Fuege alle Teile des Hubschraubers hinzu.
    tgmHelicopter.addChild(tgCabin);
    tgmHelicopter.addChild(tgRotor);
    tgmHelicopter.addChild(tgTail);

    //Definiere die Bewegung.
    int timeAcc = 300; //Die Beschleunigungs-/Abbremsphase soll jeweils 0.3 Sekunden dauern.
    //Der Hubschrauber soll etwas schraeg nach oben steigen.
    Transform3D helicopterFlightAxis = new Transform3D();
    helicopterFlightAxis.rotZ(0.4*Math.PI);

    //Das Alpha fuer die Hubschrauberbewegung.
    Alpha helicopterAlpha = new Alpha(1,Alpha.INCREASING_ENABLE+Alpha.DECREASING_ENABLE,
                                      timeFlightStart,0,timeOneWayFlight,timeAcc,
                                      timeHovering,timeOneWayFlight,timeAcc,0);



    //Die Hubschrauberbewegung
    PositionInterpolator posIPHelicopter = new PositionInterpolator(helicopterAlpha,
                                                   tgmHelicopter,helicopterFlightAxis,
                                                   0.0f,0.5f);

    posIPHelicopter.setSchedulingBounds(bounds);

    //Fuege die Bewegung zur Hubschraubertransformationsgruppe.
    tgmHelicopter.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
    tgmHelicopter.addChild(posIPHelicopter);


//*** Die Transformationsgruppe zur Positionierung des Hubschraubers auf der Plattform
    Transform3D tfHelicopter = new Transform3D();
    tfHelicopter.setTranslation(new Vector3f(0.0f,platformSize+cabinRadius,0.0f));
    TransformGroup tgHelicopter = new TransformGroup(tfHelicopter);
    tgHelicopter.addChild(tgmHelicopter);





//*** Der Hubschrauber und die Plattform bilden eine Transformatinsgruppe, ***
//*** da sie gemeinsam in der Szene positioniert werden sollen.            ***
    Transform3D tfHeliPlat = new Transform3D();
    tfHeliPlat.setTranslation(new Vector3f(0.0f,0.1f,0.0f));
    TransformGroup tgHeliPlat = new TransformGroup(tfHeliPlat);
    tgHeliPlat.addChild(tgHelicopter);
    tgHeliPlat.addChild(tgPlatform);


//*** Der Baumstamm ***

    //Eine Appearance, um den Baumstamm Braun zu faerben.
    Appearance brownApp = new Appearance();
    setToMyDefaultAppearance(brownApp,new Color3f(0.5f,0.2f,0.2f));

    //Hoehe des Baumstamms
    float trunkHeight = 0.4f;

    //Erzeugung des Baumstamms als Zylinder
    Cylinder trunk = new Cylinder(0.05f,trunkHeight,brownApp);

    //Die Transformationsgruppe des Baumstamms. Der Baumstamm bleibt
    //zunaechst im Koordinatenursprung und wird zusammen mit der Krone
    //als Baum insgesamt positioniert.
    TransformGroup tgTrunk = new TransformGroup();
    tgTrunk.addChild(trunk);


//*** Die Baumkrone ***

    //Hoehe der Baumkrone
    float leavesHeight = 0.4f;

    //Erzeugung der Baumkrone als gruener Kegel
    Cone leaves = new Cone(0.3f,leavesHeight,greenApp);

    //Eine Transformation, um die Baumkrone auf den Baumstamm zu setzen.
    Transform3D tfLeaves = new Transform3D();
    tfLeaves.setTranslation(new Vector3f(0.0f,(trunkHeight+leavesHeight)/2,0.0f));

    //Die Transformationsgruppe der Baumkrone.
    TransformGroup tgLeaves = new TransformGroup(tfLeaves);
    tgLeaves.addChild(leaves);


//*** Der Baum, der nur noch aus den Transformationsgruppen     ***
//*** fuer Baumstamm und -krone zusammengesetzt werden muss.    ***

    //Eine Transformation zur Positionierung des Baums in der Szene.
    Transform3D tfTree = new Transform3D();
    tfTree.setTranslation(new Vector3f(-0.6f,0.0f,0.0f));

    //Die Transformationsgruppe des Baums
    TransformGroup tgTree = new TransformGroup(tfTree);
    tgTree.addChild(tgTrunk);
    tgTree.addChild(tgLeaves);






//*** Die Wurzel des Graphen, der die Szene enthaelt. ***
    BranchGroup theScene = new BranchGroup();


    //Fuege den Hubschrauber und den Baum zur Szene.
    theScene.addChild(tgHeliPlat);
    theScene.addChild(tgTree);



    theScene.compile();

    //Hinzufuegen der Szene
    su.addBranchGraph(theScene);

  }


  /**
  * Erzeugt eine Standardoberflaechenstruktur in einer gewuenschten Farbe
  *
  * @param app      Die Appearance, mit der die Oberflaeche belegt werden soll
  * @param col      Die gewuenschte Farbe
  */
  public static void setToMyDefaultAppearance(Appearance app, Color3f col)
  {
    app.setMaterial(new Material(col,col,col,col,120.0f));
  }



  //Hier wird etwas Licht zu der Szene hinzugefuegt.
  public void addLight(SimpleUniverse su)
  {

    BranchGroup bgLight = new BranchGroup();

    BoundingSphere bounds = new BoundingSphere(new Point3d(0.0,0.0,0.0), 100.0);
    Color3f lightColour1 = new Color3f(1.0f,1.0f,1.0f);
    Vector3f lightDir1  = new Vector3f(-1.0f,0.0f,-0.5f);
    DirectionalLight light1 = new DirectionalLight(lightColour1, lightDir1);
    light1.setInfluencingBounds(bounds);

    bgLight.addChild(light1);
    su.addBranchGraph(bgLight);
  }




}
